// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                            avtVisItXdmfFileFormat.C                       //
// ************************************************************************* //

#include <avtVisItXdmfFileFormat.h>

#include <string>

#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkFloatArray.h>
#include <vtkIdTypeArray.h>
#include <vtkInformation.h>
#include <vtkIntArray.h>
#include <vtkRectilinearGrid.h>
#include <vtkStreamingDemandDrivenPipeline.h>
#include <vtkStructuredGrid.h>
#include <vtkUnsignedCharArray.h>
#include <vtkUnstructuredGrid.h>

#include <avtCallback.h>
#include <avtDatabaseMetaData.h>
#include <avtGhostData.h>
#include <avtStructuredDomainBoundaries.h>
#include <avtVariableCache.h>

#include <Expression.h>

#include <BadIndexException.h>
#include <DebugStream.h>
#include <InvalidVariableException.h>
#include <InvalidFilesException.h>

#include <XDMFParser.h>

// Define this symbol BEFORE including hdf5.h to indicate the HDF5 code
// in this file uses version 1.6 of the HDF5 API. This is harmless for
// versions of HDF5 before 1.8 and ensures correct compilation with
// version 1.8 and thereafter. When, and if, the HDF5 code in this file
// is explicitly upgraded to the 1.8 API, this symbol should be removed.
#define H5_USE_16_API
#include <hdf5.h>
#include <visit-hdf5.h>

#include <visit-config.h>

static int CellTypeToNodeCount[17] =
     {0, 0, 0, 3, 4, 4, 5, 6, 8, 3, 6, 8, 10, 13, 15, 20, 0};

// ****************************************************************************
//  Method: avtVisItXdmfFileFormat constructor
//
//  Programmer: brugger -- generated by xml2avt
//  Creation:   Wed Nov 14 11:28:35 PDT 2007
//
//  Modifications:
//    Eric Brugger, Wed Mar 19 12:38:46 PDT 2008
//    I added code to initialize hdfFiles.
//
// ****************************************************************************

avtVisItXdmfFileFormat::avtVisItXdmfFileFormat(const char *filename)
    : avtSTMDFileFormat(&filename, 1)
{
    fname = filename;
    fileRead = false;

    hdfFiles = new hid_t[MAX_FILES];
    for (int i = 0; i < MAX_FILES; i++)
        hdfFiles[i] = H5I_INVALID_HID;

    //
    // Turn off HDF5 error message to the terminal.
    //
    H5Eset_auto(NULL, NULL);
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat destructor
//
//  Programmer: brugger -- generated by xml2avt
//  Creation:   Wed Nov 14 11:28:35 PDT 2007
//
//  Modifications:
//    Eric Brugger, Wed Mar 19 12:38:46 PDT 2008
//    I added code to delete hdfFiles.
//
// ****************************************************************************

avtVisItXdmfFileFormat::~avtVisItXdmfFileFormat()
{
    FreeUpResources();
    delete [] hdfFiles;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::OpenFile
//
//  Purpose:
//      Open an HDF file given an index into a list of files.
//
//  Arguments:
//      f       The index of the file in the list of file names.
//
//  Returns:    A handle to the open file.
//
//  Programmer: Eric Brugger
//  Creation:   Wed Mar 19 12:38:46 PDT 2008
//
// ****************************************************************************

hid_t
avtVisItXdmfFileFormat::OpenFile(int f)
{
    //
    // Make sure this is in range.
    //
    if (f < 0 || f >= nFiles)
    {
        EXCEPTION2(BadIndexException, f, nFiles);
    }

    //
    // Check to see if the file is already open.
    //
    if (hdfFiles[f] != H5I_INVALID_HID)
    {
        UsedFile(f);
        return hdfFiles[f];
    }

    debug4 << "Opening XDMF file " << filenames[f] << endl;

    //
    // Open the file.
    //
    if ((hdfFiles[f] = H5Fopen(filenames[f], H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
    {
        avtCallback::IssueWarning("Unable to open file.");
        return 0;
    }

    RegisterFile(f);

    return hdfFiles[f];
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::OpenFile
//
//  Purpose:
//      Open an HDF file given a file name.
//
//  Arguments:
//      n       The name of the file.
//
//  Returns:    A handle to the open file.
//
//  Programmer: Eric Brugger
//  Creation:   Wed Mar 19 12:38:46 PDT 2008
//
//  Modifications:
//    Eric Brugger, Fri Jun 14 10:12:17 PDT 2019
//    I increased the maximum number of characters allowed in file and
//    dataset names from 1023 to 2047.
//
// ****************************************************************************

hid_t
avtVisItXdmfFileFormat::OpenFile(const char *n)
{
    //
    // The directory of this file is all relative to the directory of
    // the xmf file.  Reflect that here.
    //
    char name[2048];
    if (n[0] == '/')
    {
        strcpy(name, n);
    }
    else
    {
        int lastSlash = fname.find_last_of(VISIT_SLASH_STRING);
        if (lastSlash == std::string::npos)
        {
            strcpy(name, n);
        }
        else
        {
            strcpy(name, fname.substr(0, lastSlash+1).c_str());
            strcpy(name + lastSlash + 1, n); 
        }
    }

    //
    // Check if the file is already in the list.  If not then add it
    // to the list.  Note that AddFile will automatically take care of
    // overflow issues.
    //
    int fileIndex = -1;
    for (int i = 0; i < nFiles; i++)
    {
        if (strcmp(filenames[i], name) == 0)
        {
            fileIndex = i;
            break;
        }
    }

    if (fileIndex == -1)
    {
        fileIndex = AddFile(name);
    }

    //
    // Open the file.
    //
    hid_t file_id = OpenFile(fileIndex);

    return file_id;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::CloseFile
//
//  Purpose:
//      Close an HDF file, given an index into the list of open files.
//
//  Arguments:
//      f       The index of the file in the list of file names.
//
//  Programmer: Eric Brugger
//  Creation:   Wed Mar 19 12:38:46 PDT 2008
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::CloseFile(int f)
{
    if (hdfFiles[f] != H5I_INVALID_HID)
    {
        debug4 << "Closing HDF file " << filenames[f] << endl;

        if (H5Fclose(hdfFiles[f]) < 0)
        {
            avtCallback::IssueWarning("Unable to close the HDF file.");
        }
        UnregisterFile(f);
        hdfFiles[f] = H5I_INVALID_HID;
    }
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::DetermineFileAndDataset
//
//  Purpose:
//      Split a filename:dataset location into the file name and dataset name.
//      The filename and datasetname buffers must be 2048 characters long
//      including the terminating NULL.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Kathleen Bonnell, Tue Jan  8 17:51:30 PST 2008
//    Use 'const' with return of ststr to suppress error on Windows with MSVC8.
//
//    Eric Brugger, Fri Jun 14 10:12:17 PDT 2019
//    I increased the maximum number of characters allowed in file and
//    dataset names from 1023 to 2047.
//
// ****************************************************************************

int
avtVisItXdmfFileFormat::DetermineFileAndDataset(const char *input,
    char *filename, char *datasetname) const
{
    //
    // Determine the start of the filename;
    //
    const char *ptr = input;
    while (isspace(*ptr))
        ptr++;
    
    //
    // Find the ':' in the string.
    //
    const char *ptr2 = strstr(ptr, ":");
    if (ptr2 == NULL)
    {
        avtCallback::IssueWarning("Dataset specifier missing a colon.");
        return 1;
    }

    //
    // Find the end of the dataset name.
    //
    const char *ptr3 = ptr2 + 1;
    while (!isspace(*ptr3))
        ptr3++;

    //
    // Copy the filename and dataset names.
    //
    if (ptr2 - ptr == 0)
    {
        avtCallback::IssueWarning("Dataset specifier missing a filename.");
        return 2;
    }
    if (ptr2 - ptr > 2047)
    {
        avtCallback::IssueWarning("Dataset specifier filename over 2047 characters.");
        return 3;
    }
    char *ptr4 = filename;
    while (ptr != ptr2)
        *ptr4++ = *ptr++;
    *ptr4 = '\0';

    ptr2++;
    if (ptr3 - ptr2 == 0)
    {
        avtCallback::IssueWarning("Dataset specifier missing a dataset name.");
        return 4;
    }
    if (ptr3 - ptr2 > 2047)
    {
        avtCallback::IssueWarning("Dataset specifier dataset name over 2047 characters.");
        return 5;
    }
    ptr4 = datasetname;
    while (ptr2 != ptr3)
        *ptr4++ = *ptr2++;
    *ptr4 = '\0';

    return 0;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ReadXMLDataItem
//
//  Purpose:
//      Read a data item in XML format.
//
//  Programmer: Eric Brugger
//  Creation:   Thu Mar 20 16:14:36 PDT 2008
//
//  Arguments:
//      dataItem    The data item to read.
//      buf         The buffer to read the data into.
//      lBuf        The length of the buffer.
//      bufType     The data type of the buffer.
//
//  Modifications:
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I added the argument bufType.
//
// ****************************************************************************

int
avtVisItXdmfFileFormat::ReadXMLDataItem(DataItem *dataItem, void *buf, int lBuf,
    int bufType)
{
    //
    // Check that the size of the dataset and buffer match.
    //
    int ldataItem = 1;
    for (int j = 0; j < dataItem->nDims; j++)
        ldataItem *= dataItem->dims[j];
    if (ldataItem != lBuf)
    {
        avtCallback::IssueWarning("Dimensions don't match buffer size.");
        return 0;
    }

    //
    // Open the file and seek to the start of the data.
    //
    FILE *file = fopen(fname.c_str(), "r");
    fseek(file, dataItem->cdataOffset, SEEK_SET);

    //
    // Read the data.  We will read lBuf values and not make any type
    // of check to see if there is not any additional trailing stuff.
    //
    int i;
    for (i = 0; i < lBuf; i++)
    {
        //
        // Read the value.  Fscanf will skip over any leading
        // white space.
        //
        if (bufType == VTK_FLOAT)
        {
            double value;
            if (fscanf(file, "%lf", &value) == 1)
            {
                ((float *)buf)[i] = (float) value;
            }
            else
            {
                break;
            }
        }
        else
        {
            double value;
            if (fscanf(file, "%lf", &value) == 1)
            {
                ((int *)buf)[i] = (int) value;
            }
            else
            {
                break;
            }
        }
    }

    //
    // Close the file.
    //
    fclose(file);

    if (i != lBuf)
    {
        avtCallback::IssueWarning("Not enough values in the data item.");
        return 0;
    }

    return 1;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ReadHDFDataItem
//
//  Purpose:
//      Read a data item in HDF5 format.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Arguments:
//      dataItem    The data item to read.
//      buf         The buffer to read the data into.
//      lBuf        The length of the buffer.
//      bufType     The data type of the buffer.
//
//  Modifications:
//    Eric Brugger, Wed Mar 19 12:38:46 PDT 2008
//    I modified the routine so that it no longer opens and closes an
//    hdf file each time it is called.  Instead it uses OpenFile, which
//    hides the details of caching multiple open files so that files
//    typically only need to be opened once.
//
//    Eric Brugger, Thu Mar 20 10:10:15 PDT 2008
//    I added error checking for the input arguments.
//
//    Eric Brugger, Thu Mar 20 16:14:36 PDT 2008
//    I moved the error checking to ReadDataItem.
//
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I added the argument bufType.
//
//    Brad Whitlock, Fri May 16 09:52:31 PDT 2008
//    Added debugging info since it can help debug creation of XML schemas.
//
//    Eric Brugger, Tue Aug 19 15:49:00 PDT 2008
//    I added coding to convert uchar, char, uint, and int to float if
//    necessary, since HDF5 doesn't do that type of conversion.
//
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I changed the way char and int conversions are made to float since
//    the previous method caused a crash. I added coding to convert ulong
//    and long to float if necessary, when sizeof(long) was 8.
//
// ****************************************************************************

int
avtVisItXdmfFileFormat::ReadHDFDataItem(DataItem *dataItem, void *buf, int lBuf,
    int bufType)
{
    const char *mName = "avtVisItXdmfFileFormat::ReadHDFDataItem: ";
    hid_t     file_id, dataset_id, dataspace_id;
    hid_t     dataset_type;
    size_t    dataset_type_size;
    H5T_sign_t dataset_type_sign;
    H5T_class_t dataset_type_class;

    //
    // Check that the buffer type is valid.
    //
    if (bufType != VTK_FLOAT && bufType != VTK_INT)
        return 0;

    //
    // If cdata is NULL, then read it from the file.
    //
    if (dataItem->cdata == NULL)
    {
        FILE *file = fopen(fname.c_str(), "r");
        fseek(file, dataItem->cdataOffset, SEEK_SET);
        dataItem->cdata = new char[dataItem->cdataLength+1];
        fread(dataItem->cdata, 1, dataItem->cdataLength, file);
        dataItem->cdata[dataItem->cdataLength] = '\0';
        fclose(file);
    }

    //
    // Split the path into the file and dataset parts.
    //
    if (DetermineFileAndDataset(dataItem->cdata, filename, datasetname) != 0)
    {
        avtCallback::IssueWarning("Dataset specification invalid.");
        return 0;
    }
    debug4 << mName << "filename = " << filename << endl;

    //
    // Open the data file.
    //
    if ((file_id = OpenFile(filename)) < 0)
    {
        avtCallback::IssueWarning("Unable to open file.");
        return 0;
    }
    debug4 << mName << "datasetname = " << datasetname << endl;

    //
    // Open the data set.
    //
    if ((dataset_id = H5Dopen(file_id, datasetname)) < 0)
    {
        avtCallback::IssueWarning("Unable to open dataset.");
        return 0;
    }

    //
    // Get the data type information for the data set.
    //
    if ((dataset_type = H5Dget_type(dataset_id)) < 0)
    {
        avtCallback::IssueWarning("Unable to get the dataset type.");
        H5Dclose(dataset_id);
        return 0;
    }

    dataset_type_class = H5Tget_class(dataset_type);
    dataset_type_sign  = H5Tget_sign(dataset_type);
    dataset_type_size  = H5Tget_size(dataset_type);

    //
    // Check that the type is supported.  This includes 1, 4, and 8 byte
    // integers and 4 and 8 byte floats.
    //
    if ((dataset_type_class == H5T_INTEGER &&
         dataset_type_size != 1 && dataset_type_size != 4 &&
         dataset_type_size != 8) ||
        (dataset_type_class == H5T_FLOAT &&
         dataset_type_size != 4 && dataset_type_size != 8) ||
        (dataset_type_class != H5T_INTEGER &&
         dataset_type_class != H5T_FLOAT))
    {
        avtCallback::IssueWarning("Unsupported data type.");
        H5Dclose(dataset_id);
        return 0;
    }
        
    //
    // Determine the size of the dataset.
    //
    if ((dataspace_id = H5Dget_space(dataset_id)) < 0)
    {
        avtCallback::IssueWarning("Unable to get the dataspace id.");
        H5Dclose(dataset_id);
        return 0;
    }
    hssize_t ldataset = H5Sget_simple_extent_npoints(dataspace_id);
    if (H5Sclose(dataspace_id) < 0)
    {
        avtCallback::IssueWarning("Unable to close the dataspace.");
    }

    //
    // Check that the size of the dataset and the dimensions match.
    //
    int ldataItem = 1;
    debug4 << mName << "nDims = " << dataItem->nDims << "  {";
    for (int i = 0; i < dataItem->nDims; i++)
    {
        ldataItem *= dataItem->dims[i];
        debug4 << dataItem->dims[i] << " ";
    }
    debug4 << "}" << endl;
    debug4 << mName << "ldataset=" << ldataset << endl;
    if (ldataItem != ldataset)
    {
        avtCallback::IssueWarning("Dimensions don't match dataset size.");
        H5Dclose(dataset_id);
        return 0;
    }

    //
    // Check that the size of the dataset and the buffer match.
    //
    if (ldataset != lBuf)
    {
        avtCallback::IssueWarning("Buffer size doesn't match dataset size.");
        H5Dclose(dataset_id);
        return 0;
    }

    //
    // Read the data set.
    //
    if (bufType == VTK_INT && dataset_type_class == H5T_INTEGER)
    {
        debug4 << mName << "Reading integer data" << endl;
        if (H5Dread(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
                    H5P_DEFAULT, buf) < 0)
        {
            avtCallback::IssueWarning("Unable to read the dataset.");
            H5Dclose(dataset_id);
            return 0;
        }
    }
    else if (bufType == VTK_FLOAT && dataset_type_class == H5T_FLOAT)
    {
        debug4 << mName << "Reading float data" << endl;
        if (H5Dread(dataset_id, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL,
                    H5P_DEFAULT, buf) < 0)
        {
            avtCallback::IssueWarning("Unable to read the dataset.");
            H5Dclose(dataset_id);
            return 0;
        }
    }
    else if (bufType == VTK_FLOAT)
    {
        if (dataset_type_size == 1 && dataset_type_sign == H5T_SGN_NONE)
        {
            debug4 << mName << "Converting uchar to float" << endl;
            unsigned char *cBuf = new unsigned char[lBuf];
            if (H5Dread(dataset_id, H5T_NATIVE_UCHAR, H5S_ALL, H5S_ALL,
                        H5P_DEFAULT, cBuf) < 0)
            {
                avtCallback::IssueWarning("Unable to read the dataset.");
                H5Dclose(dataset_id);
                return 0;
            }
            float *fBuf = (float *)buf;
            for (int i = 0; i < lBuf; i++)
                fBuf[i] = (float) cBuf[i];
            delete [] cBuf;
        }
        else if (dataset_type_size == 1 && dataset_type_sign == H5T_SGN_2)
        {
            debug4 << mName << "Converting char to float" << endl;
            char *cBuf = new char[lBuf];
            if (H5Dread(dataset_id, H5T_NATIVE_CHAR, H5S_ALL, H5S_ALL,
                        H5P_DEFAULT, cBuf) < 0)
            {
                avtCallback::IssueWarning("Unable to read the dataset.");
                H5Dclose(dataset_id);
                return 0;
            }
            float *fBuf = (float *)buf;
            for (int i = 0; i < lBuf; i++)
                fBuf[i] = (float) cBuf[i];
            delete [] cBuf;
        }
        else if (dataset_type_size == 4 && dataset_type_sign == H5T_SGN_NONE)
        {
            debug4 << mName << "Converting uint to float" << endl;
            unsigned int *iBuf = new unsigned int[lBuf];
            if (H5Dread(dataset_id, H5T_NATIVE_UINT, H5S_ALL, H5S_ALL,
                        H5P_DEFAULT, iBuf) < 0)
            {
                avtCallback::IssueWarning("Unable to read the dataset.");
                H5Dclose(dataset_id);
                return 0;
            }
            float *fBuf = (float *)buf;
            for (int i = 0; i < lBuf; i++)
                fBuf[i] = (float) iBuf[i];
            delete [] iBuf;
        }
        else if (dataset_type_size == 4 && dataset_type_sign == H5T_SGN_2)
        {
            debug4 << mName << "Converting int to float" << endl;
            int *iBuf = new int[lBuf];
            if (H5Dread(dataset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
                        H5P_DEFAULT, iBuf) < 0)
            {
                avtCallback::IssueWarning("Unable to read the dataset.");
                H5Dclose(dataset_id);
                return 0;
            }
            float *fBuf = (float *)buf;
            for (int i = 0; i < lBuf; i++)
                fBuf[i] = (float) iBuf[i];
            delete [] iBuf;
        }
        else if (dataset_type_size == 8 && dataset_type_sign == H5T_SGN_NONE)
        {
            if (sizeof(unsigned long) == 8)
            {
                debug4 << mName << "Converting ulong to float" << endl;
                unsigned long *iBuf = new unsigned long[lBuf];
                if (H5Dread(dataset_id, H5T_NATIVE_ULONG, H5S_ALL, H5S_ALL,
                            H5P_DEFAULT, iBuf) < 0)
                {
                    avtCallback::IssueWarning("Unable to read the dataset.");
                    H5Dclose(dataset_id);
                    return 0;
                }
                float *fBuf = (float *)buf;
                for (int i = 0; i < lBuf; i++)
                    fBuf[i] = (float) iBuf[i];
                delete [] iBuf;
            }
            else
            {
                avtCallback::IssueWarning("Unable to read the dataset.");
                H5Dclose(dataset_id);
                return 0;
            }
        }
        else if (dataset_type_size == 8 && dataset_type_sign == H5T_SGN_2)
        {
            if (sizeof(long) == 8)
            {
                debug4 << mName << "Converting long to float" << endl;
                long *iBuf = new long[lBuf];
                if (H5Dread(dataset_id, H5T_NATIVE_LONG, H5S_ALL, H5S_ALL,
                            H5P_DEFAULT, iBuf) < 0)
                {
                    avtCallback::IssueWarning("Unable to read the dataset.");
                    H5Dclose(dataset_id);
                    return 0;
                }
                float *fBuf = (float *)buf;
                for (int i = 0; i < lBuf; i++)
                    fBuf[i] = (float) iBuf[i];
                delete [] iBuf;
            }
            else
            {
                avtCallback::IssueWarning("Unable to read the dataset.");
                H5Dclose(dataset_id);
                return 0;
            }
        }
        else
        {
            avtCallback::IssueWarning("Unable to read the dataset.");
            H5Dclose(dataset_id);
            return 0;
        }
    }

    //
    // Close the dataset.
    //
    if (H5Dclose(dataset_id) < 0)
    {
        avtCallback::IssueWarning("Unable to close the dataset.");
    }

    return 1;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ReadDataItem
//
//  Purpose:
//      Read a data item.
//
//  Programmer: Eric Brugger
//  Creation:   Thu Mar 20 16:14:36 PDT 2008
//
//  Arguments:
//      dataItem    The data item to read.
//      buf         The buffer to read the data into.
//      lBuf        The length of the buffer.
//      bufType     The data type of the buffer.
//
//  Modifications:
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I added the argument bufType.
//
// ****************************************************************************

int
avtVisItXdmfFileFormat::ReadDataItem(DataItem *dataItem, void *buf, int lBuf,
    int bufType)
{
    //
    // Do some error checking.
    //
    if (dataItem == NULL || buf == NULL || lBuf <= 0)
        return 0;

    //
    // Read the data based on the format.
    //
    switch (dataItem->format)
    {
      case DataItem::DATA_FORMAT_XML:
        return ReadXMLDataItem(dataItem, buf, lBuf, bufType);

        break;
      case DataItem::DATA_FORMAT_HDF:
        return ReadHDFDataItem(dataItem, buf, lBuf, bufType);

        break;
    }
    
    return 0;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::AddVarInfo
//
//  Purpose:
//      Add a VarInfo entry to the file variable list.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::AddVarInfo(bool topGrid, int iBlock, VarInfo *varInfo)
{
    //
    // Check if the varInfo already exists in the file list.
    //
    unsigned int iVar = 0;
    while (iVar < fileVarList.size() &&
           (fileVarList[iVar]->iDomain != varInfo->iDomain || 
            fileVarList[iVar]->iGrid != varInfo->iGrid || 
            fileVarList[iVar]->name != varInfo->name))
    {
        iVar++;
    }

    if (iVar == fileVarList.size())
    {
        //
        // The entry is not in the list. If this is a top level grid add
        // it straight to the list, otherwise add it as block under a new
        // top level entry.  We are adding NULL blocks to fill any gapps
        // in the block list.
        //
        if (topGrid)
        {
            fileVarList.push_back(varInfo);
        }
        else
        {
            VarInfo *varInfo2 = new VarInfo;
            varInfo2->name         = varInfo->name;
            varInfo2->meshName     = varInfo->meshName;
            varInfo2->variableType = varInfo->variableType;
            varInfo2->centering    = varInfo->centering;
            varInfo2->varDimension = varInfo->varDimension;
            varInfo2->iDomain      = varInfo->iDomain;
            varInfo2->iGrid        = varInfo->iGrid;

            varInfo2->nBlocks = iBlock + 1;
            for (int i = fileVarList.size(); i < iBlock; i++)
            {
                varInfo2->blockList.push_back(NULL);
            } 
            varInfo2->blockList.push_back(varInfo);

            fileVarList.push_back(varInfo2);
        }
    }
    else
    {
        //
        // The entry is in the list. If this is a top level grid then it
        // is a duplicate and we will ignore it, otherwise it will normally
        // be added as a new block under an existing top level entry. If the
        // block is already in the list then it is a duplicate and we will
        // ignore it. We are adding NULL blocks to fill any gapps in the
        // block list.
        //
        if (topGrid)
        {
            debug1 << "Duplicate variable name, ignoring second instance." << endl;
        }
        else
        {
            if (fileVarList[iVar]->nBlocks == iBlock + 1)
            {
                debug1 << "Duplicate variable name, ignoring second instance." << endl;
                return;
            }

            fileVarList[iVar]->nBlocks = iBlock + 1;
            for (int i = fileVarList[iVar]->blockList.size(); i < iBlock; i++)
            {
                fileVarList[iVar]->blockList.push_back(NULL);
            } 
            fileVarList[iVar]->blockList.push_back(varInfo);
        }
    }
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseDataItem
//
//  Purpose:
//      Parse a DataItem node in the DOM tree, returning a pointer to a
//      DataItem.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
//    Eric Brugger, Fri Jun 14 10:12:17 PDT 2019
//    I increased the maximum number of characters allowed in file and
//    dataset names from 1023 to 2047.
//
// ****************************************************************************

DataItem *
avtVisItXdmfFileFormat::ParseDataItem()
{
    std::string    dimensions;
    std::string    numberType;
    std::string    precision;
    std::string    format;
    char     *cdata=NULL;
    int       cdataOffset=0;
    int       cdataLength=0;

    //
    // Process the attributes in the start tag.
    //
    while (xdmfParser.GetNextAttribute())
    {
        if (strcmp(xdmfParser.GetAttributeName(), "DIMENSIONS") == 0)
        {
            dimensions = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "TYPE") == 0 ||
                 strcmp(xdmfParser.GetAttributeName(), "NUMBERTYPE") == 0)
        {
            numberType = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "PRECISION") == 0)
        {
            precision = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "FORMAT") == 0)
        {
            format = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
    }

    //
    // Process the rest of the information.
    //
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            xdmfParser.SkipToEndTag();
        }
        else if (elementType == XDMFParser::TYPE_CDATA)
        {
            cdataOffset = xdmfParser.GetCDataOffset();
            cdataLength = xdmfParser.GetCDataLength();
            if (cdataLength <= 2047)
            {
                cdata = new char[cdataLength+1];
                memcpy(cdata, xdmfParser.GetCDataValue(), cdataLength+1);
            }
        }
        else if (elementType == XDMFParser::TYPE_END_TAG)
        {
            break;
        }
        elementType = xdmfParser.GetNextElement();
    }

    DataItem *dataItem = new DataItem;

    //
    // Store the CData. We do this first so that it will get freed
    // as part of the DataItem destructor.
    //
    if (cdataLength == 0)
    {
        debug1 << "Invalid DataItem: CData missing." << endl;
        delete dataItem;
        return NULL;
    }
    dataItem->cdata = cdata;
    dataItem->cdataOffset = cdataOffset;
    dataItem->cdataLength = cdataLength;
    
    //
    // Determine the format (HDF or XML).
    //
    if (format == "HDF")
        dataItem->format = DataItem::DATA_FORMAT_HDF; 
    else if (format == "" || format == "XML")
        dataItem->format = DataItem::DATA_FORMAT_XML; 
    else
    {
        debug1 << "Invalid DataItem: Format must be XML or HDF." << endl;
        delete dataItem;
        return NULL;
    }

    //
    // Determine the number type.
    //
    if (numberType == "" || numberType == "FLOAT")
        dataItem->type = DataItem::DATA_TYPE_FLOAT;
    else if (numberType == "INT")
        dataItem->type = DataItem::DATA_TYPE_INT;
    else if (numberType == "UINT")
        dataItem->type = DataItem::DATA_TYPE_UINT;
    else if (numberType == "CHAR")
        dataItem->type = DataItem::DATA_TYPE_CHAR;
    else if (numberType == "UCHAR")
        dataItem->type = DataItem::DATA_TYPE_UCHAR;
    else
    {
        debug1 << "Invalid DataItem: NumberType must be Float, Int, UInt, Char, or UChar." << endl;
        delete dataItem;
        return NULL;
    }

    //
    // Determine the precision.
    //
    if (precision == "" || precision == "4")
        dataItem->precision = 4;
    else if (precision == "8")
        dataItem->precision = 8;
    else if (precision == "1")
        dataItem->precision = 1;
    else
    {
        debug1 << "Invalid DataItem: Precision must be 1, 4, or 8." << endl;
        delete dataItem;
        return NULL;
    }

    //
    // Determine the dimensions.
    //
    if (dimensions == "")
    {
        debug1 << "Invalid DataItem: Dimensions missing." << endl;
        delete dataItem;
        return NULL;
    }
    dataItem->nDims = sscanf(dimensions.c_str(), "%d %d %d %d",
           &dataItem->dims[0],
           &dataItem->dims[1],
           &dataItem->dims[2],
           &dataItem->dims[3]);
    if (dataItem->nDims < 1)
    {
        debug1 << "Invalid DataItem: Dimensions must have at least 1 value." << endl;
        delete dataItem;
        return NULL;
    }

    return dataItem;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseTopology
//
//  Purpose:
//      Parse a Topology node in the DOM tree.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Arguments:
//      topologyType      The topology type string.
//      numberOfElements  The number of elements string
//      nodesPerElement   The number of nodes per element string.
//      baseOffset        The base offset for the connectivity.
//      order             The order of the nodes for the element.
//      topologyData      The data item associated with the topology.
//
//  Modifications:
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I added the arguments nodesPerElement and topology data.
//
//    Eric Brugger, Tue Apr  8 14:25:31 PDT 2008
//    Added the arguments baseOffset and order.
//
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseTopology(std::string &topologyType,
    std::string &numberOfElements, std::string &nodesPerElement, std::string &baseOffset,
    std::string &order, DataItem **topologyData)
{
    //
    // Process the attributes in the start tag.
    //
    while (xdmfParser.GetNextAttribute())
    {
        if (strcmp(xdmfParser.GetAttributeName(), "TYPE") == 0 ||
            strcmp(xdmfParser.GetAttributeName(), "TOPOLOGYTYPE") == 0)
        {
            topologyType = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "NUMBEROFELEMENTS") == 0 ||
                 strcmp(xdmfParser.GetAttributeName(), "DIMENSIONS") == 0)
        {
            numberOfElements = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "NODESPERELEMENT") == 0)
        {
            nodesPerElement = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "BASEOFFSET") == 0)
        {
            baseOffset = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "ORDER") == 0)
        {
            order = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
    }

    //
    // Process the rest of the information.
    //
    bool haveTopologyData = false;
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            if (strcmp(xdmfParser.GetElementName(), "DATAITEM") == 0)
            {
                if (!haveTopologyData)
                {
                    *topologyData = ParseDataItem();
                    haveTopologyData = true;
                }
                else
                {
                    xdmfParser.SkipToEndTag();
                }
            }
            else
            {
                xdmfParser.SkipToEndTag();
            }
        }
        else if (elementType == XDMFParser::TYPE_END_TAG)
        {
            break;
        }
        elementType = xdmfParser.GetNextElement();
    }
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseGeometry
//
//  Purpose:
//      Parse a Geometry node in the DOM tree.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Kathleen Bonnell, Tue Jan  8 17:51:30 PST 2008
//    Remove 'struct' from in front of DataItem, to remove compile error on 
//    Windows.
//
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseGeometry(std::string &geometryType, int &nDataItem,
    DataItem **geometryData)
{
    //
    // Process the attributes in the start tag.
    //
    while (xdmfParser.GetNextAttribute())
    {
        if (strcmp(xdmfParser.GetAttributeName(), "TYPE") == 0 ||
            strcmp(xdmfParser.GetAttributeName(), "GEOMETRYTYPE") == 0)
        {
            geometryType = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
    }

    //
    // Process the rest of the information.
    //
    nDataItem = 0;
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            if (strcmp(xdmfParser.GetElementName(), "DATAITEM") == 0)
            {
                if (nDataItem < 3)
                {
                    geometryData[nDataItem] = ParseDataItem();
                    nDataItem++;
                }
                else
                {
                    xdmfParser.SkipToEndTag();
                }
            }
            else
            {
                xdmfParser.SkipToEndTag();
            }
        }
        else if (elementType == XDMFParser::TYPE_END_TAG)
        {
            break;
        }
        elementType = xdmfParser.GetNextElement();
    }
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseAttribute
//
//  Purpose:
//      Parse an Attribute node in the DOM tree.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
// ****************************************************************************

VarInfo *
avtVisItXdmfFileFormat::ParseAttribute(int iDomain, int iGrid,
    const std::string &gridName)
{
    std::string    attributeName;
    std::string    attributeType;
    std::string    center;
    DataItem *varData=NULL;
    
    //
    // Process the attributes in the start tag.
    //
    while (xdmfParser.GetNextAttribute())
    {
        if (strcmp(xdmfParser.GetAttributeName(), "NAME") == 0)
        {
            attributeName = std::string(xdmfParser.GetAttributeValue());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "TYPE") == 0 ||
                 strcmp(xdmfParser.GetAttributeName(), "ATTRIBUTETYPE") == 0)
        {
            attributeType = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "CENTER") == 0)
        {
            center = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
    }

    //
    // Process the rest of the information.
    //
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            if (strcmp(xdmfParser.GetElementName(), "DATAITEM") == 0)
            {
                varData = ParseDataItem();
            }
            else
            {
                xdmfParser.SkipToEndTag();
            }
        }
        else if (elementType == XDMFParser::TYPE_END_TAG)
        {
            break;
        }
        elementType = xdmfParser.GetNextElement();
    }

    //
    // Do some error checking.
    //
    if (attributeName == "")
    {
        debug1 << "Invalid Attribute: Name missing." << endl;
        return NULL;
    }

    if (varData == NULL)
    {
        debug1 << "Invalid Attribute: Data missing." << endl;
        return NULL;
    }

    VarInfo *varInfo = new VarInfo;

    //
    // Store the DataItem. We do this first so that it will get freed
    // as part of the VarInfo destructor.
    //
    varInfo->varData = varData;

    //
    // Set some identifying information for the variable.
    //
    varInfo->name = attributeName;
    varInfo->meshName = gridName;
    varInfo->iDomain = iDomain;
    varInfo->iGrid = iGrid;

    //
    // Determine the attribute type and dimension.
    //
    if (attributeType == "" || attributeType == "SCALAR")
    {
        varInfo->variableType = VarInfo::TYPE_SCALAR;
        varInfo->varDimension = 1;
    }
    else if (attributeType == "VECTOR")
    {
        varInfo->variableType = VarInfo::TYPE_VECTOR;
        varInfo->varDimension = varData->dims[varData->nDims-1];
    }
    else if (attributeType == "TENSOR")
    {
        varInfo->variableType = VarInfo::TYPE_TENSOR;
        varInfo->varDimension = 9;
    }
    else if (attributeType == "TENSOR6")
    {
        varInfo->variableType = VarInfo::TYPE_TENSOR;
        varInfo->varDimension = 6;
    }
    else if (attributeType == "MATRIX")
    {
        debug1 << "Invalid Attribute: Unsupported AttributeType - Matrix." << endl;
        delete varInfo;
        return NULL;
    }
    else
    {
        debug1 << "Invalid Attribute: Invalid AttributeType." << endl;
        delete varInfo;
        return NULL;
    }

    //
    // Determine the centering.
    //
    if (center == "" || center == "NODE")
    {
        varInfo->centering = AVT_NODECENT;
    }
    else if (center == "CELL")
    {
        varInfo->centering = AVT_ZONECENT;
    }
    else if (center == "FACE" || center == "EDGE" || center == "GRID")
    {
        debug1 << "Invalid Attribute: Unsupported Center." << endl;
        delete varInfo;
        return NULL;
    }
    else
    {
        debug1 << "Invalid Attribute: Invalid Center." << endl;
        delete varInfo;
        return NULL;
    }

    varInfo->nBlocks = 1;
    varInfo->extents = NULL;

    return varInfo;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseGridInformation
//
//  Purpose:
//      Parse an Information node within a grid node in the DOM tree.
//
//  Programmer: Eric Brugger
//  Creation:   Mon Mar 17 13:22:09 PDT 2008
//
//  Modifications:
//    Eric Brugger, Tue Aug 26 15:57:05 PDT 2008
//    Modified the routine to handle grid information with a baseIndex.
//
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseGridInformation(std::string &baseIndex,
    std::string &ghostOffsets)
{
    bool haveBaseIndex = false;
    bool haveGhostOffsets = false;

    //
    // Process the attributes in the start tag.
    //
    while (xdmfParser.GetNextAttribute())
    {
        if (strcmp(xdmfParser.GetAttributeName(), "NAME") == 0)
        {
            if (strcmp(xdmfParser.GetAttributeValueAsUpper(), "BASEINDEX") == 0)
                haveBaseIndex = true;
            else if (strcmp(xdmfParser.GetAttributeValueAsUpper(), "GHOSTOFFSETS") == 0)
                haveGhostOffsets = true;
        }
        else if (strcmp(xdmfParser.GetAttributeName(), "VALUE") == 0)
        {
            if (haveBaseIndex)
                baseIndex = std::string(xdmfParser.GetAttributeValueAsUpper());
            else if (haveGhostOffsets)
                ghostOffsets = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
    }

    //
    // Process the rest of the information.
    //
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            xdmfParser.SkipToEndTag();
        }
        else if (elementType == XDMFParser::TYPE_END_TAG)
        {
            break;
        }
        elementType = xdmfParser.GetNextElement();
    }
}

// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseTime
//
//  Purpose:
//      Parse a Time node in the DOM tree.
//
//  Programmer: Mark C. Miller
//  Creation:   Wed Jul 29 18:08:15 PDT 2009
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseTime(std::string &time)
{
    while (xdmfParser.GetNextAttribute())
    {
        if (strcmp(xdmfParser.GetAttributeName(), "VALUE") == 0)
        {
            time = std::string(xdmfParser.GetAttributeValueAsUpper());
        }
    }

    //
    // Process the rest of the information.
    //
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            xdmfParser.SkipToEndTag();
        }
        else if (elementType == XDMFParser::TYPE_END_TAG)
        {
            break;
        }
        elementType = xdmfParser.GetNextElement();
    }
}

// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseUniformGrid
//
//  Purpose:
//      Parse a uniform grid node of the DOM tree.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Eric Brugger, Mon Mar 17 13:22:09 PDT 2008
//    Added logic to read ghost offsets.
//
//    Eric Brugger, Thu Mar 20 10:10:15 PDT 2008
//    I changed some of the geometry type strings that were incorrect.
//
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I added additional coding to fully handle unstructured grids.
//
//    Eric Brugger, Tue Apr  8 14:25:31 PDT 2008
//    I added coding to handle baseOffset and order for unstructured grids.
//
//    Eric Brugger, Tue Aug 26 15:57:05 PDT 2008
//    Modified the routine to handle grid information with a baseIndex. I
//    also reversed the order in which the ghost zone indices are interpreted
//    to match the order in which dimensions are interpreted.
//
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
//    Mark C. Miller, Thu Jul 30 11:12:17 PDT 2009
//    Added parsing of <Time Value="xxx" /> element.
//
//    Eric Brugger, Mon Feb  1 15:12:29 PST 2010
//    Allowed the geometry type TYPE_X_Y_Z to have only 2 arrays.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseUniformGrid(std::vector<MeshInfo*> &meshList,
    int iDomain, int iGrid, bool topGrid,
    const std::string &gridName)
{
    std::string    topologyType;
    std::string    numberOfElements;
    std::string    nodesPerElement;
    std::string    baseOffset;
    std::string    order;
    DataItem *topologyData = NULL;
    std::string    geometryType;
    int       nMeshData;
    DataItem *meshData[3];
    VarInfo  *varInfo = NULL;
    std::vector<VarInfo*> varList;
    std::string    baseIndex;
    std::string    ghostOffsets;
    std::string    time;

    //
    // Process the elements of the grid.
    //
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            if (strcmp(xdmfParser.GetElementName(), "TOPOLOGY") == 0)
            {
                ParseTopology(topologyType, numberOfElements, nodesPerElement,
                    baseOffset, order, &topologyData);
            }
            else if (strcmp(xdmfParser.GetElementName(), "GEOMETRY") == 0)
            {
                ParseGeometry(geometryType, nMeshData, meshData);
            }
            else if (strcmp(xdmfParser.GetElementName(), "ATTRIBUTE") == 0)
            {
                if ((varInfo = ParseAttribute(iDomain, iGrid, gridName)) != NULL)
                    varList.push_back(varInfo);
            }
            else if (strcmp(xdmfParser.GetElementName(), "INFORMATION") == 0)
            {
                ParseGridInformation(baseIndex, ghostOffsets);
            }
            else if (strcmp(xdmfParser.GetElementName(), "TIME") == 0)
            {
                ParseTime(time);
            }
            else
            {
                xdmfParser.SkipToEndTag();
            }
        }
        else if (elementType == XDMFParser::TYPE_END_TAG)
        {
            break;
        }
        elementType = xdmfParser.GetNextElement();
    }

    //
    // Do some error checking.
    //
    if (topologyType == "")
    {
        debug1 << gridName << ": Invalid Grid elmement - "
               << "Topology type missing." << endl;
        for (unsigned int i = 0; i < varList.size(); i++)
            delete varList[i];
        return;
    }

    if (numberOfElements == "")
    {
        debug1 << gridName << ": Invalid Grid elmement - "
               << "Topology number of elements missing." << endl;
        for (unsigned int i = 0; i < varList.size(); i++)
            delete varList[i];
        return;
    }

    MeshInfo *meshInfo = new MeshInfo;

    if (topGrid)
    {
        meshInfo->name = gridName;
        meshInfo->iDomain = iDomain;
    }

    meshInfo->nBlocks = 1;
    meshInfo->extents = NULL;

    if (geometryType == "" || geometryType == "XYZ")
        meshInfo->geometryType = MeshInfo::TYPE_XYZ;
    else if (geometryType == "XY")
        meshInfo->geometryType = MeshInfo::TYPE_XY;
    else if (geometryType == "X_Y_Z")
        meshInfo->geometryType = MeshInfo::TYPE_X_Y_Z;
    else if (geometryType == "VXVYVZ")
        meshInfo->geometryType = MeshInfo::TYPE_VXVYVZ;
    else if (geometryType == "ORIGIN_DXDYDZ")
        meshInfo->geometryType = MeshInfo::TYPE_ORIGIN_DXDYDZ;
    else
    {
        debug1 << gridName << ": Invalid Grid elmement - "
               << "Illegal GeometryType." << endl;
        delete meshInfo;
        for (unsigned int i = 0; i < varList.size(); i++)
            delete varList[i];
        return;
    }

    if (topologyType == "2DRECTMESH" || topologyType == "2DCORECTMESH")
    {
        meshInfo->topologicalDimension = 2;
        meshInfo->spatialDimension = 2;
        meshInfo->type = AVT_RECTILINEAR_MESH;
    }
    else if (topologyType == "2DSMESH")
    {
        meshInfo->topologicalDimension = 2;
        meshInfo->spatialDimension = 2;
        meshInfo->type = AVT_CURVILINEAR_MESH;
    }
    else if (topologyType == "3DRECTMESH" || topologyType == "3DCORECTMESH")
    {
        meshInfo->topologicalDimension = 3;
        meshInfo->spatialDimension = 3;
        meshInfo->type = AVT_RECTILINEAR_MESH;
    }
    else if (topologyType == "3DSMESH")
    {
        meshInfo->topologicalDimension = 3;
        meshInfo->spatialDimension = 3;
        meshInfo->type = AVT_CURVILINEAR_MESH;
    }
    else
    {
        if (topologyType == "POLYVERTEX")
            meshInfo->cellType = MeshInfo::CELL_POLYVERTEX;
        else if (topologyType == "POLYLINE")
            meshInfo->cellType = MeshInfo::CELL_POLYLINE;
        else if (topologyType == "POLYGON")
            meshInfo->cellType = MeshInfo::CELL_POLYGON;
        else if (topologyType == "TRIANGLE")
            meshInfo->cellType = MeshInfo::CELL_TRIANGLE;
        else if (topologyType == "QUADRILATERAL")
            meshInfo->cellType = MeshInfo::CELL_QUADRILATERAL;
        else if (topologyType == "TETRAHEDRON")
            meshInfo->cellType = MeshInfo::CELL_TETRAHEDRON;
        else if (topologyType == "PYRAMID")
            meshInfo->cellType = MeshInfo::CELL_PYRAMID;
        else if (topologyType == "WEDGE")
            meshInfo->cellType = MeshInfo::CELL_WEDGE;
        else if (topologyType == "HEXAHEDRON")
            meshInfo->cellType = MeshInfo::CELL_HEXAHEDRON;
        else if (topologyType == "EDGE_3")
            meshInfo->cellType = MeshInfo::CELL_EDGE_3;
        else if (topologyType == "TRIANGLE_6")
            meshInfo->cellType = MeshInfo::CELL_TRIANGLE_6;
        else if (topologyType == "QUADRILATERAL_8")
            meshInfo->cellType = MeshInfo::CELL_QUADRILATERAL_8;
        else if (topologyType == "TETRAHEDRON_10")
            meshInfo->cellType = MeshInfo::CELL_TETRAHEDRON_10;
        else if (topologyType == "PYRAMID_13")
            meshInfo->cellType = MeshInfo::CELL_PYRAMID_13;
        else if (topologyType == "WEDGE_15")
            meshInfo->cellType = MeshInfo::CELL_WEDGE_15;
        else if (topologyType == "HEXAHEDRON_20")
            meshInfo->cellType = MeshInfo::CELL_HEXAHEDRON_20;
        else if (topologyType == "MIXED")
            meshInfo->cellType = MeshInfo::CELL_MIXED;
        else
        {
            debug1 << gridName << ": Invalid Grid elmement - "
                   << "Illegal Topology." << endl;
            delete meshInfo;
            for (unsigned int i = 0; i < varList.size(); i++)
                delete varList[i];
            return;
        }

        if (meshInfo->geometryType == MeshInfo::TYPE_XY)
        {
            meshInfo->topologicalDimension = 2;
            meshInfo->spatialDimension = 2;
        }
        else if (meshInfo->geometryType == MeshInfo::TYPE_VXVYVZ ||
                 meshInfo->geometryType == MeshInfo::TYPE_X_Y_Z)
        {
            meshInfo->topologicalDimension = nMeshData;
            meshInfo->spatialDimension = nMeshData;
        }
        else
        {
            meshInfo->topologicalDimension = 3;
            meshInfo->spatialDimension = 3;
        }
        meshInfo->type = AVT_UNSTRUCTURED_MESH;
    }

    meshInfo->dimensions[0] = 1;
    meshInfo->dimensions[1] = 1;
    meshInfo->dimensions[2] = 1;
    if (meshInfo->type == AVT_UNSTRUCTURED_MESH)
    {
        if (topologyData == NULL)
        {
            debug1 << gridName << ": Invalid Grid elmement - "
                   << "Topology data item missing." << endl;
            delete meshInfo;
            for (unsigned int i = 0; i < varList.size(); i++)
                delete varList[i];
            return;
        }

        meshInfo->topologyData = topologyData;

        //
        // In both cases below we store the dimensions in dimensions[1]
        // and the nodes per element in dimensions[0].  This way we know
        // where they are without any complex logic.
        //
        if (meshInfo->cellType == MeshInfo::CELL_POLYVERTEX ||
            meshInfo->cellType == MeshInfo::CELL_POLYLINE ||
            meshInfo->cellType == MeshInfo::CELL_POLYGON)
        {
            if (nodesPerElement == "")
            {
                debug1 << gridName << ": Invalid Grid elmement - "
                       << "Topology nodes per element missing."
                       << endl;
                delete meshInfo;
                for (unsigned int i = 0; i < varList.size(); i++)
                    delete varList[i];
                return;
            }

            sscanf(numberOfElements.c_str(), "%d", &meshInfo->dimensions[1]);
            sscanf(nodesPerElement.c_str(), "%d", &meshInfo->dimensions[0]);
        }
        else
        {
            sscanf(numberOfElements.c_str(), "%d", &meshInfo->dimensions[1]);

            meshInfo->baseOffset = 0;
            if (baseOffset != "")
            {
                sscanf(baseOffset.c_str(), "%d", &meshInfo->baseOffset);
            }

            int nNodes = CellTypeToNodeCount[meshInfo->cellType];
            if (order != "" && nNodes != 0)
            {
                meshInfo->order = new int[nNodes];
                char *str = NULL, *str2 = NULL;
                int iVal = (int)strtol(order.c_str(), &str, 10);
                meshInfo->order[0] = (iVal < 0) ? 0 :
                    ((iVal < nNodes) ? iVal : nNodes - 1);
                for (int i = 1; i < nNodes; i++)
                {
                    iVal = (int)strtol(str, &str2, 10);
                    meshInfo->order[i] = (iVal < 0) ? 0 :
                        ((iVal < nNodes) ? iVal : nNodes - 1);
                    str = str2;
                }
            }
        }
    }
    else
    {
        if (meshInfo->topologicalDimension == 2)
        {
            sscanf(numberOfElements.c_str(), "%d %d",
                   &meshInfo->dimensions[1], &meshInfo->dimensions[0]);
        }
        else
        {
            sscanf(numberOfElements.c_str(), "%d %d %d",
                   &meshInfo->dimensions[2], &meshInfo->dimensions[1],
                   &meshInfo->dimensions[0]);
        }
    }

    if (baseIndex != "")
    {
        meshInfo->baseIndex = new int[3];
        for (int i = 0; i < 3; i++)
            meshInfo->baseIndex[i] = 0;
        if (meshInfo->topologicalDimension == 2)
        {
            sscanf(baseIndex.c_str(), "%d %d",
                &meshInfo->baseIndex[1], &meshInfo->baseIndex[0]);
        }
        else
        {
            sscanf(baseIndex.c_str(), "%d %d %d", &meshInfo->baseIndex[2],
                &meshInfo->baseIndex[1], &meshInfo->baseIndex[0]);
        }
    }

    if (ghostOffsets != "")
    {
        meshInfo->ghostOffsets = new int[6];
        for (int i = 0; i < 6; i++)
            meshInfo->ghostOffsets[i] = 0;
        if (meshInfo->topologicalDimension == 2)
        {
            sscanf(ghostOffsets.c_str(), "%d %d %d %d",
                &meshInfo->ghostOffsets[2], &meshInfo->ghostOffsets[3],
                &meshInfo->ghostOffsets[0], &meshInfo->ghostOffsets[1]);
        }
        else
        {
            sscanf(ghostOffsets.c_str(), "%d %d %d %d %d %d",
                &meshInfo->ghostOffsets[4], &meshInfo->ghostOffsets[5],
                &meshInfo->ghostOffsets[2], &meshInfo->ghostOffsets[3],
                &meshInfo->ghostOffsets[0], &meshInfo->ghostOffsets[1]);
        }
    }

    if (time != "")
    {
        sscanf(time.c_str(), "%lf", &meshInfo->time);
    }

    for (int i = 0; i < nMeshData; i++)
        meshInfo->meshData[i] = meshData[i];

    meshList.push_back(meshInfo);

    for (unsigned int i = 0; i < varList.size(); i++)
        AddVarInfo(topGrid, meshList.size()-1, varList[i]);
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseGrid
//
//  Purpose:
//      Parse a grid node of the DOM tree.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseGrid(std::vector<MeshInfo*> &meshList,
    int iDomain, int iGrid, std::string &gridName, bool topGrid)
{
    std::string    gridType;

    //
    // If this is the top grid then we want both the name and grid type
    // from the attribute list, if it isn't then we want to use the grid
    // passed in and get the grid type from the attribute list. It is
    // done this way because we want to use the top grid name for all the
    // sub grids.
    //
    if (topGrid)
    {
        //
        // Process the attributes in the start tag.
        //
        while (xdmfParser.GetNextAttribute())
        {
            if (strcmp(xdmfParser.GetAttributeName(), "NAME") == 0)
            {
                gridName = std::string(xdmfParser.GetAttributeValue());
            }
            else if (strcmp(xdmfParser.GetAttributeName(), "TYPE") == 0 ||
                     strcmp(xdmfParser.GetAttributeName(), "GRIDTYPE") == 0)
            {
                gridType = std::string(xdmfParser.GetAttributeValueAsUpper());
            }
        }

        //
        // If no name was given, create a default one.
        //
        if (gridName == "")
        {
            char str[16];
            sprintf(str, "Grid%04d", iGrid);
            gridName = str;
        }

        //
        // Check that the grid name is unique and skip it if it isn't.
        //
        for (unsigned int i = 0; i < meshList.size(); i++)
        {
            if (meshList[i]->iDomain == iDomain &&
                meshList[i]->name == gridName)
            {
                debug1 << "Duplicate mesh name, ignoring second instance." << endl;
                xdmfParser.SkipToEndTag();
                return;
            }
        }
    }
    else
    {
        //
        // Process the attributes in the start tag.
        //
        while (xdmfParser.GetNextAttribute())
        {
            if (strcmp(xdmfParser.GetAttributeName(), "TYPE") == 0 ||
                strcmp(xdmfParser.GetAttributeName(), "GRIDTYPE") == 0)
            {
                gridType = std::string(xdmfParser.GetAttributeValueAsUpper());
            }
        }
    }

    if (gridType == "" || gridType == "UNIFORM")
    {
        ParseUniformGrid(meshList, iDomain, iGrid,
            topGrid, gridName);
    }
    else if (gridType == "COLLECTION" || gridType == "TREE")
    {
        if (topGrid)
        {
            topGrid = false;
            MeshInfo *meshInfo = new MeshInfo;

            XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
            while (elementType != XDMFParser::TYPE_EOF)
            {
                if (elementType == XDMFParser::TYPE_START_TAG)
                {
                    if (strcmp(xdmfParser.GetElementName(), "GRID") == 0)
                    {
                        ParseGrid(meshInfo->blockList, iDomain, iGrid,
                                  gridName, topGrid);
                    }
                    else
                    {
                        xdmfParser.SkipToEndTag();
                    }
                }
                else if (elementType == XDMFParser::TYPE_END_TAG)
                {
                    break;
                }
                elementType = xdmfParser.GetNextElement();
            }

            if (meshInfo->blockList.size() == 0)
            {
                debug1 << gridName << ": Invalid Tree/Collection - no valid blocks"
                       << endl;
                delete meshInfo;
                return;
            }

            meshInfo->name = gridName;
            meshInfo->nBlocks = meshInfo->blockList.size();
            meshInfo->extents = NULL;

            meshInfo->topologicalDimension = meshInfo->blockList[0]->topologicalDimension;
            meshInfo->spatialDimension = meshInfo->blockList[0]->spatialDimension;
            meshInfo->type = meshInfo->blockList[0]->type;

            meshList.push_back(meshInfo);
        }
        else
        {
            XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
            while (elementType != XDMFParser::TYPE_EOF)
            {
                if (elementType == XDMFParser::TYPE_START_TAG)
                {
                    if (strcmp(xdmfParser.GetElementName(), "GRID") == 0)
                    {
                        ParseGrid(meshList, iDomain, iGrid, gridName,
                                  topGrid);
                    }
                    else
                    {
                        xdmfParser.SkipToEndTag();
                    }
                }
                else if (elementType == XDMFParser::TYPE_END_TAG)
                {
                    break;
                }
                elementType = xdmfParser.GetNextElement();
            }
        }
    }
    else if (gridType == "SUBSET")
    {
        debug1 << gridName << ": Unsupported Grid type - Subset" << endl;
        return;
    }
    else
    {
        debug1 << gridName << ": Invalid Grid type" << endl;
        return;
    }
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseDomain
//
//  Purpose:
//      Parse a domain node of the DOM tree.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseDomain(int iDomain)
{
    std::string    domainName;

    //
    // Process the attributes in the start tag.
    //
    while (xdmfParser.GetNextAttribute())
    {
        if (strcmp(xdmfParser.GetAttributeName(), "NAME") == 0)
        {
            domainName = std::string(xdmfParser.GetAttributeValue());
        }
    }

    //
    // If no name was given, create a default one.
    //
    if (domainName == "")
    {
        char str[16];
        sprintf(str, "Domain%04d", iDomain);
        domainName = str;
    }

    DomainInfo *domainInfo = new DomainInfo;

    domainInfo->name = domainName;
    fileDomainList.push_back(domainInfo);

    //
    // Process the rest of the information.
    //
    int iGrid = 0;
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            if (strcmp(xdmfParser.GetElementName(), "GRID") == 0)
            {
                std::string gridName;

                ParseGrid(fileMeshList, iDomain, iGrid, gridName, true);

                iGrid++;
            }
            else
            {
                xdmfParser.SkipToEndTag();
            }
        }
        else if (elementType == XDMFParser::TYPE_END_TAG)
        {
            break;
        }
        elementType = xdmfParser.GetNextElement();
    }
    fileDomainList[iDomain]->nGrids = iGrid;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseXdmf
//
//  Purpose:
//      Parse a xdmf node of the DOM tree.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseXdmf()
{
    //
    // Skip the attributes in the start tag.
    //
    while (xdmfParser.GetNextAttribute()) /* do nothing */;

    //
    // Process the rest of the information.
    //
    int nDomains = 0;
    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            if (strcmp(xdmfParser.GetElementName(), "DOMAIN") == 0)
            {
                ParseDomain(nDomains);

                nDomains++;
            }
            else
            {
                xdmfParser.SkipToEndTag();
            }
        }
        elementType = xdmfParser.GetNextElement();
    }
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ParseXMLFile
//
//  Purpose:
//      Parse the XML file, storing its contents in internal data structures
//      for efficient reading of data.  It gets the DOM for the XML file
//      to parse the file and then frees the DOM at the end.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 16 13:08:36 PST 2007
//
//  Modifications:
//    Eric Brugger, Fri Apr 24 08:31:34 PDT 2009
//    I enhanced the routine to be insensitive to the case for element names,
//    attribute names, and attribute values that were not names.
//
//    Jeremy Meredith, Thu Jan  7 12:32:55 EST 2010
//    An XML file can't start with random text.  It has to be a comment
//    or open tag.  We used to "successfully" parse any old file here.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ParseXMLFile(void)
{
    xdmfParser.SetInputFileName(fname.c_str());

    XDMFParser::ElementType elementType = xdmfParser.GetNextElement();
    if (elementType == XDMFParser::TYPE_CDATA)
    {
        // If the first thing in the file isn't an open tag or a comment,
        // it's not and XML file.  Error out.
        EXCEPTION2(InvalidFilesException, fname.c_str(),
                   "Not an XML format file.");
    }
    while (elementType != XDMFParser::TYPE_EOF)
    {
        if (elementType == XDMFParser::TYPE_START_TAG)
        {
            if (strcmp(xdmfParser.GetElementName(), "XDMF") == 0)
            {
                ParseXdmf();
            }
            else
            {
                xdmfParser.SkipToEndTag();
            }
        }
        elementType = xdmfParser.GetNextElement();
    }
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::FreeUpResources
//
//  Purpose:
//      When VisIt is done focusing on a particular timestep, it asks that
//      timestep to free up any resources (memory, file descriptors) that
//      it has associated with it.  This method is the mechanism for doing
//      that.
//
//  Programmer: brugger -- generated by xml2avt
//  Creation:   Wed Nov 14 11:28:35 PDT 2007
//
//  Modifications:
//    Eric Brugger, Wed Mar 19 12:38:46 PDT 2008
//    I added code to close any open hdf files.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::FreeUpResources(void)
{
    debug4 << "XDMF File Format instructed to free up resources." << endl;

    for (int i = 0; i < nFiles; i++)
        CloseFile(i);

    for (unsigned int i = 0; i < fileMeshList.size(); i++)
        delete fileMeshList[i];
    fileMeshList.clear();
    for (unsigned int i = 0; i < fileVarList.size(); i++)
        delete fileVarList[i];
    fileVarList.clear();
    for (unsigned int i = 0; i < fileDomainList.size(); i++)
        delete fileDomainList[i];
    fileDomainList.clear();

    fileRead = false;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::ActivateTimestep
//
//  Purpose: Provides a guarenteed collective entry point for operations
//    that may involve collective parallel communication.
//
//  Programmer: brugger
//  Creation:   Wed Nov 14 11:28:35 PDT 2007
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::ActivateTimestep(void)
{
    //
    // Return if the data has been read.
    //
    if (fileRead)
        return;

    //
    // Read the XML file.
    //
    ParseXMLFile();

    //
    // Patch up the variable and mesh names to prepend the domain name
    // and mesh name if necessary to ensure unique names.
    //
    if (fileDomainList.size() == 1)
    {
        for (unsigned int i = 0; i < fileVarList.size(); i++)
        {
            VarInfo *varInfo = fileVarList[i];

            if (fileDomainList[varInfo->iDomain]->nGrids > 1)
            {
                varInfo->name = varInfo->meshName + "/" + varInfo->name;
            }
        }
    }
    else
    {
        for (unsigned int i = 0; i < fileMeshList.size(); i++)
        {
            MeshInfo *meshInfo = fileMeshList[i];

            meshInfo->name = fileDomainList[meshInfo->iDomain]->name + "/" +
                             meshInfo->name;
        }

        for (unsigned int i = 0; i < fileVarList.size(); i++)
        {
            VarInfo *varInfo = fileVarList[i];

            if (fileDomainList[varInfo->iDomain]->nGrids == 1)
            {
                varInfo->name = fileDomainList[varInfo->iDomain]->name + "/" +
                                varInfo->name;
            }
            else
            {
                varInfo->name = fileDomainList[varInfo->iDomain]->name + "/" +
                                varInfo->meshName + "/" + varInfo->name;
            }
            varInfo->meshName = fileDomainList[varInfo->iDomain]->name + "/" +
                                varInfo->meshName;
        }
    }

    fileRead = true;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::PopulateDatabaseMetaData
//
//  Purpose:
//      This database meta-data object is like a table of contents for the
//      file.  By populating it, you are telling the rest of VisIt what
//      information it can request from you.
//
//  Programmer: brugger -- generated by xml2avt
//  Creation:   Wed Nov 14 11:28:35 PDT 2007
//
//  Modifications:
//    Eric Brugger, Mon Apr 14 16:50:42 PDT 2008
//    Added support for tensors.
//
//    Eric Brugger, Tue Aug 26 15:57:05 PDT 2008
//    Modified the routine to add structured domain boundary information
//    for multiblock rectilinear or curvilinear meshes.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    //
    // Set the mesh information.
    //
    for (unsigned int i = 0; i < fileMeshList.size(); i++)
    {
        MeshInfo *meshInfo = fileMeshList[i];

        AddMeshToMetaData(md, meshInfo->name, meshInfo->type,
            meshInfo->extents, meshInfo->nBlocks, 0,
            meshInfo->spatialDimension, meshInfo->topologicalDimension);

        if (!avtDatabase::OnlyServeUpMetaData() && meshInfo->nBlocks > 1)
        {
            //
            // Check that baseIndex is defined on all the submeshes and
            // that the mesh is either pure rectilinear or pure curvilinear.
            //
            avtMeshType meshType = meshInfo->blockList[0]->type;
            int iBlock = 0;
            while (iBlock < meshInfo->nBlocks)
            {
                if (meshInfo->blockList[iBlock]->baseIndex == NULL ||
                    meshInfo->blockList[iBlock]->type != meshType)
                    break;
                iBlock++;
            }

            //
            // Create the appropriate domain boundary information.
            //
            if (meshType == AVT_RECTILINEAR_MESH &&
                iBlock == meshInfo->nBlocks)
            {
                avtRectilinearDomainBoundaries *rdb =
                    new avtRectilinearDomainBoundaries(true);

                rdb->SetNumDomains(meshInfo->nBlocks);
                for (int j = 0; j < meshInfo->nBlocks; j++)
                {
                    int extents[6];

                    extents[0] = meshInfo->blockList[j]->baseIndex[0];
                    extents[1] = meshInfo->blockList[j]->baseIndex[0] +
                                 meshInfo->blockList[j]->dimensions[0] - 1;
                    extents[2] = meshInfo->blockList[j]->baseIndex[1];
                    extents[3] = meshInfo->blockList[j]->baseIndex[1] +
                                 meshInfo->blockList[j]->dimensions[1] - 1;
                    extents[4] = meshInfo->blockList[j]->baseIndex[2];
                    extents[5] = meshInfo->blockList[j]->baseIndex[2] +
                                 meshInfo->blockList[j]->dimensions[2] - 1;

                    rdb->SetIndicesForRectGrid(j, extents);
                }
                rdb->CalculateBoundaries();

                void_ref_ptr vr = void_ref_ptr(rdb,
                    avtStructuredDomainBoundaries::Destruct);
                cache->CacheVoidRef("any_mesh",
                    AUXILIARY_DATA_DOMAIN_BOUNDARY_INFORMATION, -1, -1, vr);
            }
            else if (meshType == AVT_CURVILINEAR_MESH &&
                iBlock == meshInfo->nBlocks)
            {
                avtCurvilinearDomainBoundaries *cdb =
                    new avtCurvilinearDomainBoundaries(true);

                cdb->SetNumDomains(meshInfo->nBlocks);
                for (int j = 0; j < meshInfo->nBlocks; j++)
                {
                    int extents[6];

                    extents[0] = meshInfo->blockList[j]->baseIndex[0];
                    extents[1] = meshInfo->blockList[j]->baseIndex[0] +
                                 meshInfo->blockList[j]->dimensions[0] - 1;
                    extents[2] = meshInfo->blockList[j]->baseIndex[1];
                    extents[3] = meshInfo->blockList[j]->baseIndex[1] +
                                 meshInfo->blockList[j]->dimensions[1] - 1;
                    extents[4] = meshInfo->blockList[j]->baseIndex[2];
                    extents[5] = meshInfo->blockList[j]->baseIndex[2] +
                                 meshInfo->blockList[j]->dimensions[2] - 1;

                    cdb->SetIndicesForRectGrid(j, extents);
                }
                cdb->CalculateBoundaries();

                void_ref_ptr vr = void_ref_ptr(cdb,
                    avtStructuredDomainBoundaries::Destruct);
                cache->CacheVoidRef("any_mesh",
                    AUXILIARY_DATA_DOMAIN_BOUNDARY_INFORMATION, -1, -1, vr);
            }
        }
    }   

    //
    // Set the variable information.  Handle scalars, vectors and tensors
    // all at the same time.
    //
    for (unsigned int i = 0; i < fileVarList.size(); i++)
    {
        VarInfo *varInfo = fileVarList[i];

        switch (varInfo->variableType)
        {
          case VarInfo::TYPE_SCALAR:
            AddScalarVarToMetaData(md, varInfo->name, varInfo->meshName,
                varInfo->centering);
            break;

          case VarInfo::TYPE_VECTOR:
            AddVectorVarToMetaData(md, varInfo->name, varInfo->meshName,
                varInfo->centering, varInfo->varDimension);
            break;

          case VarInfo::TYPE_TENSOR:
            if (varInfo->varDimension == 6)
                AddSymmetricTensorVarToMetaData(md, varInfo->name,
                    varInfo->meshName, varInfo->centering,
                    varInfo->varDimension);
            else
                AddTensorVarToMetaData(md, varInfo->name, varInfo->meshName,
                    varInfo->centering, varInfo->varDimension);
            break;

          default:
            AddTensorVarToMetaData(md, varInfo->name, varInfo->meshName,
                varInfo->centering, varInfo->varDimension);
            break;
        }
    }   
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetStructuredGhostZones
//
//  Purpose:
//    Retrieves ghost zone information from the structured mesh and adds it
//    to the dataset.
//
//  Arguments:
//      meshInfo    The MeshInfo structure describing the mesh.
//      ds          The vtkDataSet in which to store the ghost level
//                  information.
//
//  Programmer: Eric Brugger
//  Creation:   Mon Mar 17 13:22:09 PDT 2008
//
//  Modifications:
//    Kathleen Biagas, Mon Aug 15 14:09:55 PDT 2016
//    VTK-8, API for updating GhostLevel changed.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::GetStructuredGhostZones(MeshInfo *meshInfo, vtkDataSet *ds)
{
    //
    // Return if we don't have any ghost zone information.
    //
    if (meshInfo->ghostOffsets == NULL)
        return;

    unsigned char realVal = 0;
    unsigned char ghostVal = 0;
    avtGhostData::AddGhostZoneType(ghostVal,
        DUPLICATED_ZONE_INTERNAL_TO_PROBLEM);
    int ncells = ds->GetNumberOfCells();
    vtkUnsignedCharArray *ghostCells = vtkUnsignedCharArray::New();
    ghostCells->SetName("avtGhostZones");
    ghostCells->SetNumberOfComponents(1);
    ghostCells->SetNumberOfTuples(ncells);

    unsigned char *buf = ghostCells->GetPointer(0);
    for (int i = 0; i < ncells; i++)
        buf[i] = ghostVal;

    int iMin[3], iMax[3];
    for (int i = 0; i < 3; i++)
    {
        iMin[i] = meshInfo->ghostOffsets[i*2];
        iMax[i] = meshInfo->dimensions[i] - 1 - meshInfo->ghostOffsets[i*2+1];
    }
    for (int k = iMin[2]; k < iMax[2]; k++)
    {
        for (int j = iMin[1]; j < iMax[1]; j++)
        {
            for (int i = iMin[0]; i < iMax[0]; i++)
            {
                int ndx = k * (meshInfo->dimensions[1]-1) *
                              (meshInfo->dimensions[0]-1) +
                          j * (meshInfo->dimensions[0]-1) + i;
                buf[ndx] = realVal;
            }
        }
    }
    ds->GetCellData()->AddArray(ghostCells);
    ghostCells->Delete();

    vtkIntArray *realDims = vtkIntArray::New();
    realDims->SetName("avtRealDims");
    realDims->SetNumberOfValues(6);
    realDims->SetValue(0, iMin[0]);
    realDims->SetValue(1, iMax[0]);
    realDims->SetValue(2, iMin[1]);
    realDims->SetValue(3, iMax[1]);
    realDims->SetValue(4, iMin[2]);
    realDims->SetValue(5, iMax[2]);
    ds->GetFieldData()->AddArray(realDims);
    ds->GetFieldData()->CopyFieldOn("avtRealDims");
    realDims->Delete();

    ds->GetInformation()->Set(
        vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(), 0);
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetPoints
//
//  Purpose:
//      Gets the points associated with this mesh info.  The points are
//      returned as a vtkPoints.
//
//  Arguments:
//      meshInfo    The MeshInfo structure describing the mesh.
//      nnodes      The number of nodes in the mesh.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Mar 21 15:22:11 PDT 2008
//
//  Modifications:
//    Brad Whitlock, Fri May 16 09:50:56 PDT 2008
//    Fixed pointer arithmetic bug in TYPE_X_Y_Z code.
//
//    Eric Brugger, Mon Feb  1 15:12:29 PST 2010
//    Swapped the logic for the geometry type TYPE_X_Y_Z for TYPE_VXVYVZ
//    since they were handled backwards.
//
// ****************************************************************************

vtkPoints *
avtVisItXdmfFileFormat::GetPoints(MeshInfo *meshInfo, int nnodes)
{
    vtkPoints            *points  = vtkPoints::New();

    points->SetNumberOfPoints(nnodes);
    float *pts = (float *) points->GetVoidPointer(0);

    switch (meshInfo->geometryType)
    {
      case MeshInfo::TYPE_XYZ:
        if (!ReadDataItem(meshInfo->meshData[0], pts, 3 * nnodes, VTK_FLOAT))
        {
            points->Delete();
            return NULL;
        }
        break;

      case MeshInfo::TYPE_XY:
        {
            float *coords = new float[2*nnodes];
            if (!ReadDataItem(meshInfo->meshData[0], coords, 2 * nnodes,
                VTK_FLOAT))
            {
                delete [] coords;
                points->Delete();
                return NULL;
            }
            for (int i = 0; i < nnodes; i++)
            {
                *pts++ = *coords++;
                *pts++ = *coords++;
                *pts++ = 0.;
            }
            delete [] coords;
        }
        break;

      case MeshInfo::TYPE_X_Y_Z:
        {
            float *xcoords = new float[nnodes];
            float *ycoords = new float[nnodes];
            if (!ReadDataItem(meshInfo->meshData[0], xcoords, nnodes,
                VTK_FLOAT))
            {
                delete [] xcoords; delete [] ycoords;
                points->Delete();
                return NULL;
            }
            if (!ReadDataItem(meshInfo->meshData[1], ycoords, nnodes,
                VTK_FLOAT))
            {
                delete [] xcoords; delete [] ycoords;
                points->Delete();
                return NULL;
            }
            if (meshInfo->meshData[2] == NULL)
            {
                float *xc = xcoords;
                float *yc = ycoords;
                for (int i = 0; i < nnodes; i++)
                {
                    *pts++ = *xc++;
                    *pts++ = *yc++;
                    *pts++ = 0.;
                }
            }
            else
            {
                float *zcoords = new float[nnodes];
                float *xc = xcoords;
                float *yc = ycoords;
                float *zc = zcoords;
                if (!ReadDataItem(meshInfo->meshData[2], zcoords, nnodes,
                    VTK_FLOAT))
                {
                    delete [] xcoords; delete [] ycoords; delete [] zcoords;
                    points->Delete();
                    return NULL;
                }
                for (int i = 0; i < nnodes; i++)
                {
                    *pts++ = *xc++;
                    *pts++ = *yc++;
                    *pts++ = *zc++;
                }
                delete [] zcoords;
            }
            delete [] xcoords;
            delete [] ycoords;
        }
        break;

      default:
        avtCallback::IssueWarning("GetMesh: Invalid GeometryType.");
        break;
    }

    return points;
}

// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetRectilinearMesh
//
//  Purpose:
//      Gets the rectilinear mesh associated with this mesh info.  The mesh
//      is returned as a vtkRectilinearGrid.
//
//  Arguments:
//      meshInfo    The MeshInfo structure describing the mesh.
//
//  Programmer: Eric Brugger
//  Creation:   Thu Mar 20 08:26:23 PDT 2008
//
//  Modifications:
//    Eric Brugger, Thu Mar 20 16:14:36 PDT 2008
//    I replaced ReadHDFDataItem with ReadDataItem.
//
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I added a buffer data type argument to the calls to ReadDataItem.
//
//    Eric Brugger, Thu Oct  2 16:05:58 PDT 2008
//    I added code to add base index information to the data set if it is
//    present.
//
//    Eric Brugger, Mon Feb  1 15:12:29 PST 2010
//    Swapped the logic for the geometry type TYPE_VXVYVZ for TYPE_X_Y_Z
//    since they were handled backwards.
//
// ****************************************************************************

vtkDataSet *
avtVisItXdmfFileFormat::GetRectilinearMesh(MeshInfo *meshInfo)
{
    //
    // Create the VTK object.
    //
    vtkRectilinearGrid   *rgrid   = vtkRectilinearGrid::New();

    //
    // Tell the grid what its dimensions are and populate the points array.
    //
    rgrid->SetDimensions(meshInfo->dimensions);

    vtkFloatArray *xcoords = vtkFloatArray::New();
    xcoords->SetNumberOfTuples(meshInfo->dimensions[0]);
    float *xpts = xcoords->GetPointer(0);
    vtkFloatArray *ycoords = vtkFloatArray::New();
    ycoords->SetNumberOfTuples(meshInfo->dimensions[1]);
    float *ypts = ycoords->GetPointer(0);
    vtkFloatArray *zcoords = vtkFloatArray::New();
    zcoords->SetNumberOfTuples(meshInfo->dimensions[2]);
    float *zpts = zcoords->GetPointer(0);

    rgrid->SetXCoordinates(xcoords);
    xcoords->Delete();
    rgrid->SetYCoordinates(ycoords);
    ycoords->Delete();
    rgrid->SetZCoordinates(zcoords);
    zcoords->Delete();

    //
    // Populate the coordinates.  Put in 3D points with z=0 if the
    // mesh is 2D.
    //
    float origin[3], dxdydz[3];

    switch (meshInfo->geometryType)
    {
      case MeshInfo::TYPE_VXVYVZ:
        if (!ReadDataItem(meshInfo->meshData[0], xpts, meshInfo->dimensions[0],
            VTK_FLOAT))
        {
            rgrid->Delete();
            return NULL;
        }
        if (!ReadDataItem(meshInfo->meshData[1], ypts, meshInfo->dimensions[1],
            VTK_FLOAT))
        {
            rgrid->Delete();
            return NULL;
        }
        if (meshInfo->meshData[2] == NULL)
        {
            zpts[0] = 0.;
        }
        else
        {
            if (!ReadDataItem(meshInfo->meshData[2], zpts,
                meshInfo->dimensions[2], VTK_FLOAT))
            {
                rgrid->Delete();
                return NULL;
            }
        }
        break;

      case MeshInfo::TYPE_ORIGIN_DXDYDZ:
        if (!ReadDataItem(meshInfo->meshData[0], origin, 3, VTK_FLOAT))
        {
            rgrid->Delete();
            return NULL;
        }
        if (!ReadDataItem(meshInfo->meshData[1], dxdydz, 3, VTK_FLOAT))
        {
            rgrid->Delete();
            return NULL;
        }
        for (int i = 0; i < meshInfo->dimensions[0]; i++)
            xpts[i] = origin[0] + i * dxdydz[0];
        for (int j = 0; j < meshInfo->dimensions[1]; j++)
            ypts[j] = origin[1] + j * dxdydz[1];
        for (int k = 0; k < meshInfo->dimensions[2]; k++)
            zpts[k] = origin[2] + k * dxdydz[2];

        break;

      default:
        avtCallback::IssueWarning("GetMesh: Invalid GeometryType.");
        break;
    }

    //
    // If we have base indices, then add that to the dataset as field data.
    //
    if (meshInfo->baseIndex != NULL)
    {
        vtkIntArray *arr = vtkIntArray::New();
        arr->SetNumberOfTuples(3);
        arr->SetValue(0, meshInfo->baseIndex[0]);
        arr->SetValue(1, meshInfo->baseIndex[1]);
        arr->SetValue(2, meshInfo->baseIndex[2]);
        arr->SetName("base_index");
        rgrid->GetFieldData()->AddArray(arr);
        arr->Delete();
    }

    return rgrid;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetCurvilinearMesh
//
//  Purpose:
//      Gets the curvilinear mesh associated with this mesh info.  The mesh
//      is returned as a vtkStructuredGrid.
//
//  Arguments:
//      meshInfo    The MeshInfo structure describing the mesh.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Nov 30 13:10:08 PST 2007
//
//  Modifications:
//    Eric Brugger, Thu Mar 20 10:10:15 PDT 2008
//    Corrected the coding for the case where the geomtery type was
//    TYPE_X_Y_Z and there were only 2 coordinate arrays.
//
//    Eric Brugger, Thu Mar 20 16:14:36 PDT 2008
//    I replaced ReadHDFDataItem with ReadDataItem.
//
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I replaced some code with a call to the function GetPoints.
//
//    Brad Whitlock, Fri May 16 09:52:31 PDT 2008
//    Added debugging info since it can help debug creation of XML schemas.
//
//    Eric Brugger, Thu Oct  2 16:05:58 PDT 2008
//    I added code to add base index information to the data set if it is
//    present.
//
// ****************************************************************************

vtkDataSet *
avtVisItXdmfFileFormat::GetCurvilinearMesh(MeshInfo *meshInfo)
{
    const char *mName = "avtVisItXdmfFileFormat::GetCurvilinearMesh: ";

    //
    // Populate the coordinates.  Put in 3D points with z=0 if the
    // mesh is 2D.
    //
    int nnodes = meshInfo->dimensions[0] * meshInfo->dimensions[1] *
                 meshInfo->dimensions[2];
    debug4 << mName << "nnodes=" << nnodes << endl;
    debug4 << mName << "dims[] = {"
       << meshInfo->dimensions[0]
       << ", " << meshInfo->dimensions[1]
       << ", " << meshInfo->dimensions[2] << endl;

    vtkPoints *points = GetPoints(meshInfo, nnodes);
    if (points == NULL)
        return NULL;

    //
    // Create the grid and connect it to the points.
    //
    vtkStructuredGrid *sgrid = vtkStructuredGrid::New();
    sgrid->SetPoints(points);
    points->Delete();

    //
    // Tell the grid what its dimensions are.
    //
    sgrid->SetDimensions(meshInfo->dimensions);

    //
    // If we have base indices, then add that to the dataset as field data.
    //
    if (meshInfo->baseIndex != NULL)
    {
        vtkIntArray *arr = vtkIntArray::New();
        arr->SetNumberOfTuples(3);
        arr->SetValue(0, meshInfo->baseIndex[0]);
        arr->SetValue(1, meshInfo->baseIndex[1]);
        arr->SetValue(2, meshInfo->baseIndex[2]);
        arr->SetName("base_index");
        sgrid->GetFieldData()->AddArray(arr);
        arr->Delete();
    }

    return sgrid;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::PopulateCellInformation
//
//  Purpose:
//      Populates the cell information for an unstructured grid.
//
//  Arguments:
//      ugrid          The unstructured grid to add the cell information to.
//      connectivity   The cell connectivity.
//      lConnectivity  The length of the cell connectivity.
//      mixed          Flag indicating if the connectivity contains mixed
//                     cell types.
//      nCells         The number of cells.
//      vtkCellType    The type of the cell if it is not mixed.
//      nodesPerCell   The number of nodes per cell if it is not mixed.
//      baseOffset     The base offset for the connectivity.
//      order          The order of the nodes in the cell.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Mar 21 15:22:11 PDT 2008
//
//  Modifications:
//    Eric Brugger, Tue Apr  8 14:25:31 PDT 2008
//    Added the arguments baseOffset and order.
//
//    Tom Fogal, Thu Jul 21 15:46:34 MDT 2011
//    vtkIdType fix.
//
// ****************************************************************************

void
avtVisItXdmfFileFormat::PopulateCellInformation(vtkUnstructuredGrid *ugrid,
    int *connectivity, int lConnectivity, bool mixed, int nCells,
    int vtkCellType, int nodesPerCell, int baseOffset, int *order)
{
    vtkIdTypeArray *nlist = vtkIdTypeArray::New();
    if (mixed)
        nlist->SetNumberOfValues(lConnectivity);
    else
        nlist->SetNumberOfValues(nCells * (nodesPerCell + 1));
    vtkIdType *nl = nlist->GetPointer(0);

    vtkUnsignedCharArray *cellTypes = vtkUnsignedCharArray::New();
    cellTypes->SetNumberOfValues(nCells);
    unsigned char *ct = cellTypes->GetPointer(0);

    vtkIdTypeArray *cellLocations = vtkIdTypeArray::New();
    cellLocations->SetNumberOfValues(nCells);
    vtkIdType *cl = cellLocations->GetPointer(0);

    int iCell = 0;
    int index = 0;

    if (mixed)
    {
        for (int j = 0; j < nCells; j++)
        {
            int cellType = connectivity[index++];
            int vtkCellType;
            int cellCount;
            switch (cellType)
            {
              case 0x1: // Polyvertex
                vtkCellType = VTK_POLY_VERTEX;
                cellCount = connectivity[index++];
                break;
              case 0x2: // Polyline
                vtkCellType = VTK_POLY_LINE;
                cellCount = connectivity[index++];
                break;
              case 0x3: // Polygon
                vtkCellType = VTK_POLYGON;
                cellCount = connectivity[index++];
                break;
              case 0x4: // Tri
                vtkCellType = VTK_TRIANGLE;
                cellCount = 3;
                break;
              case 0x5: // Quad
                vtkCellType = VTK_QUAD;
                cellCount = 4;
                break;
              case 0x6: // Tet
                vtkCellType = VTK_TETRA;
                cellCount = 4;
                break;
              case 0x7: // Pyramid
                vtkCellType = VTK_PYRAMID;
                cellCount = 5;
                break;
              case 0x8: // Wedge
                vtkCellType = VTK_WEDGE;
                cellCount = 6;
                break;
              case 0x9: // Hex
                vtkCellType = VTK_HEXAHEDRON;
                cellCount = 8;
                break;
              case 0x0022: // Edge 3
                vtkCellType = VTK_QUADRATIC_EDGE;
                cellCount = 3;
                break;
              case 0x0024: // Triangle 6
                vtkCellType = VTK_QUADRATIC_TRIANGLE;
                cellCount = 6;
                break;
              case 0x0025: // Quad 8
                vtkCellType = VTK_QUADRATIC_QUAD;
                cellCount = 8;
                break;
              case 0x0026: // Tet 10
                vtkCellType = VTK_QUADRATIC_TETRA;
                cellCount = 10;
                break;
              case 0x0027: // Pyramid 13
                vtkCellType = VTK_QUADRATIC_PYRAMID;
                cellCount = 13;
                break;
              case 0x0028: // Wedge 15
                vtkCellType = VTK_QUADRATIC_WEDGE;
                cellCount = 15;
                break;
              case 0x0029: // Hex 20
                vtkCellType = VTK_QUADRATIC_HEXAHEDRON;
                cellCount = 20;
                break;
              default:
                vtkCellType = 0;
                cellCount = 0;
                avtCallback::IssueWarning("GetMesh: Invalid GeometryType.");
                break;
            }
            *ct++ = vtkCellType;
            *cl++ = iCell;
            *nl++ = cellCount;
            for (int i = 0; i < cellCount; i++)
                *nl++ = connectivity[index++] - baseOffset;

            iCell += cellCount + 1;
        }
        nlist->Resize(iCell);
    }
    else
    {
        if (order == NULL)
        {
            for (int j = 0; j < nCells; j++)
            {
                *ct++ = vtkCellType;
                *cl++ = iCell;
                *nl++ = nodesPerCell;
                for (int i = 0; i < nodesPerCell; i++)
                    *nl++ = connectivity[index++] - baseOffset;

                iCell += nodesPerCell + 1;
            }
        }
        else
        {
            for (int j = 0; j < nCells; j++)
            {
                *ct++ = vtkCellType;
                *cl++ = iCell;
                *nl++ = nodesPerCell;
                for (int i = 0; i < nodesPerCell; i++)
                    nl[order[i]] = connectivity[index++] - baseOffset;
                nl += nodesPerCell;

                iCell += nodesPerCell + 1;
            }
        }
    }

    vtkCellArray *cells = vtkCellArray::New();
    cells->SetCells(nCells, nlist);
    nlist->Delete();

    ugrid->SetCells(cellTypes, cellLocations, cells);
    cellTypes->Delete();
    cellLocations->Delete();
    cells->Delete();
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetUnstructuredMesh
//
//  Purpose:
//      Gets the unstructured mesh associated with this mesh info.  The mesh
//      is returned as a vtkUnstructuredGrid.
//
//  Arguments:
//      meshInfo    The MeshInfo structure describing the mesh.
//
//  Programmer: Eric Brugger
//  Creation:   Fri Mar 21 15:22:11 PDT 2008
//
//  Modifications:
//    Eric Brugger, Tue Apr  8 14:25:31 PDT 2008
//    I added coding to handle baseOffset and order for unstructured grids.
//
// ****************************************************************************

vtkDataSet *
avtVisItXdmfFileFormat::GetUnstructuredMesh(MeshInfo *meshInfo)
{
    //
    // Populate the coordinates.  Put in 3D points with z=0 if the
    // mesh is 2D.
    //
    int nnodes = 1;
    if (meshInfo->geometryType == MeshInfo::TYPE_XYZ ||
        meshInfo->geometryType == MeshInfo::TYPE_XY)
    {
        for (int i = 0; i < meshInfo->meshData[0]->nDims-1; i++)
            nnodes *= meshInfo->meshData[0]->dims[i];
    }
    else
    {
        for (int i = 0; i < meshInfo->meshData[0]->nDims; i++)
            nnodes *= meshInfo->meshData[0]->dims[i];
    }

    vtkPoints *points = GetPoints(meshInfo, nnodes);
    if (points == NULL)
        return NULL;

    //
    // Create the grid and connect it to the points.
    //
    vtkUnstructuredGrid *ugrid = vtkUnstructuredGrid::New();
    ugrid->SetPoints(points);
    points->Delete();

    //
    // Read the connectivity.
    //
    int lConnect = 1;
    for (int i = 0; i < meshInfo->topologyData->nDims; i++)
        lConnect *= meshInfo->topologyData->dims[i];
    int nCells = meshInfo->dimensions[1];
    int nodesPerCell = meshInfo->dimensions[0];
    int baseOffset = meshInfo->baseOffset;
    int *order = meshInfo->order;
    
    int *connectivity = new int[lConnect];

    if (!ReadDataItem(meshInfo->topologyData, connectivity, lConnect, VTK_INT))
    {
        ugrid->Delete();
        delete [] connectivity;
    }

    //
    // Create the cell structures.
    //
    switch (meshInfo->cellType)
    {
      case MeshInfo::CELL_POLYVERTEX:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_POLY_VERTEX, nodesPerCell, baseOffset, order);
        break;

      case MeshInfo::CELL_POLYLINE:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_POLY_LINE, nodesPerCell, baseOffset, order);
        break;

      case MeshInfo::CELL_POLYGON:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_POLYGON, nodesPerCell, baseOffset, order);
        break;

      case MeshInfo::CELL_TRIANGLE:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_TRIANGLE, 3, baseOffset, order);
        break;

      case MeshInfo::CELL_QUADRILATERAL:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_QUAD, 4, baseOffset, order);
        break;

      case MeshInfo::CELL_TETRAHEDRON:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_TETRA, 4, baseOffset, order);
        break;

      case MeshInfo::CELL_PYRAMID:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_PYRAMID, 5, baseOffset, order);
        break;

      case MeshInfo::CELL_WEDGE:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_WEDGE, 6, baseOffset, order);
        break;

      case MeshInfo::CELL_HEXAHEDRON:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_HEXAHEDRON, 8, baseOffset, order);
        break;

      case MeshInfo::CELL_EDGE_3:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_QUADRATIC_EDGE, 3, baseOffset, order);
        break;

      case MeshInfo::CELL_TRIANGLE_6:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_QUADRATIC_TRIANGLE, 6, baseOffset, order);
        break;

      case MeshInfo::CELL_QUADRILATERAL_8:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_QUADRATIC_QUAD, 8, baseOffset, order);
        break;

      case MeshInfo::CELL_TETRAHEDRON_10:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_QUADRATIC_TETRA, 10, baseOffset, order);
        break;

      case MeshInfo::CELL_PYRAMID_13:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_QUADRATIC_PYRAMID, 13, baseOffset, order);
        break;

      case MeshInfo::CELL_WEDGE_15:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_QUADRATIC_WEDGE, 15, baseOffset, order);
        break;

      case MeshInfo::CELL_HEXAHEDRON_20:
        PopulateCellInformation(ugrid, connectivity, lConnect, false, nCells,
            VTK_QUADRATIC_HEXAHEDRON, 20, baseOffset, order);
        break;

      case MeshInfo::CELL_MIXED:
        PopulateCellInformation(ugrid, connectivity, lConnect, true, nCells,
            0, 0, baseOffset, order);
        break;

      default:
        avtCallback::IssueWarning("GetMesh: Invalid GeometryType.");
        ugrid->Delete();
        delete [] connectivity;
        break;
    }

    delete [] connectivity;

    return ugrid;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetMesh
//
//  Purpose:
//      Gets the mesh associated with this file.  The mesh is returned as a
//      derived type of vtkDataSet (ie vtkRectilinearGrid, vtkStructuredGrid,
//      vtkUnstructuredGrid, etc).
//
//  Arguments:
//      domain      The index of the domain.  If there are NDomains, this
//                  value is guaranteed to be between 0 and NDomains-1,
//                  regardless of block origin.
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: brugger -- generated by xml2avt
//  Creation:   Wed Nov 14 11:28:35 PDT 2007
//
//  Modifications:
//    Eric Brugger, Mon Mar 17 13:22:09 PDT 2008
//    Added code to add ghost zone information.
//
//    Eric Brugger, Thu Mar 20 08:26:23 PDT 2008
//    Added code to handle rectilinear meshes.
//
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    Added code to handle unstructured meshes.
//
// ****************************************************************************

vtkDataSet *
avtVisItXdmfFileFormat::GetMesh(int domain, const char *meshname)
{
    debug2 << "avtVisItXdmfFileFormat::GetMesh: domain = " << domain
           << ",meshname = " << meshname << endl;

    //
    // Determine the mesh to return.
    //
    MeshInfo *meshInfo = NULL;
    for (unsigned int i = 0; i < fileMeshList.size(); i++)
    {
        if (strcmp(fileMeshList[i]->name.c_str(), meshname) == 0)
        {
            meshInfo = fileMeshList[i];
            break;
        }
    }
    if (meshInfo != NULL && meshInfo->blockList.size() > 0)
    {
        if (domain >= meshInfo->blockList.size())
        {
            debug1 << "GetMesh: Invalid domain number." << endl;
            return NULL;
        }
        else
        {
            meshInfo = meshInfo->blockList[domain];
        }
    }
    if (meshInfo == NULL)
    {
        debug1 << "GetMesh: Invalid mesh name." << endl;
        return NULL;
    }

    //
    // Get the dataset.
    //
    vtkDataSet *ds = NULL;
    switch (meshInfo->type)
    {
      case AVT_RECTILINEAR_MESH:
        ds = GetRectilinearMesh(meshInfo);
        break;
      case AVT_CURVILINEAR_MESH:
        ds = GetCurvilinearMesh(meshInfo);
        break;
      case AVT_UNSTRUCTURED_MESH:
        ds = GetUnstructuredMesh(meshInfo);
        break;
      default:
        break;
    }
    
    //
    // If we have a structured mesh, get the ghost zone infomation.
    //
    if (meshInfo->type == AVT_RECTILINEAR_MESH ||
        meshInfo->type == AVT_CURVILINEAR_MESH)
        GetStructuredGhostZones(meshInfo, ds);

    return ds;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetVar
//
//  Purpose:
//      Gets a scalar variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      domain     The index of the domain.  If there are NDomains, this
//                 value is guaranteed to be between 0 and NDomains-1,
//                 regardless of block origin.
//      varname    The name of the variable requested.
//
//  Programmer: brugger -- generated by xml2avt
//  Creation:   Wed Nov 14 11:28:35 PDT 2007
//
//  Modifications:
//    Eric Brugger, Thu Mar 20 16:14:36 PDT 2008
//    I replaced ReadHDFDataItem with ReadDataItem.
//
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I added a buffer data type argument to the call to ReadDataItem.
//
// ****************************************************************************

vtkDataArray *
avtVisItXdmfFileFormat::GetVar(int domain, const char *varname)
{
    debug2 << "avtVisItXdmfFileFormat::GetVar: domain = " << domain
           << ",varname = " << varname << endl;

    //
    // Determine the variable to return.
    //
    VarInfo *varInfo = NULL;
    for (unsigned int i = 0; i < fileVarList.size(); i++)
    {
        if (strcmp(fileVarList[i]->name.c_str(), varname) == 0)
        {
            varInfo = fileVarList[i];
            break;
        }
    }
    if (varInfo == NULL)
    {
        avtCallback::IssueWarning("GetVar: Invalid variable name.");
        return NULL;
    }
    if (varInfo->blockList.size() > 0)
    {
        if (domain >= varInfo->blockList.size())
        {
            return NULL;
        }
        else
        {
            varInfo = varInfo->blockList[domain];
            if (varInfo == NULL)
            {
                return NULL;
            }
        }
    }

    //
    // Determine the size of the data.
    //
    int ntuples = 1;
    for (int i = 0; i < varInfo->varData->nDims; i++)
        ntuples *= varInfo->varData->dims[i];

    //
    // Create the data array.
    //
    vtkFloatArray *rv = vtkFloatArray::New();
    rv->SetNumberOfTuples(ntuples);

    //
    // Read the data.
    //
    void *buf = rv->GetVoidPointer(0);
    if (!ReadDataItem(varInfo->varData, buf, ntuples, VTK_FLOAT))
    {
        rv->Delete();
        rv = NULL;
    }

    return rv;
}


// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetVectorVar
//
//  Purpose:
//      Gets a vector variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      domain     The index of the domain.  If there are NDomains, this
//                 value is guaranteed to be between 0 and NDomains-1,
//                 regardless of block origin.
//      varname    The name of the variable requested.
//
//  Programmer: brugger -- generated by xml2avt
//  Creation:   Wed Nov 14 11:28:35 PDT 2007
//
//  Modifications:
//    Eric Brugger, Thu Mar 20 16:14:36 PDT 2008
//    I replaced ReadHDFDataItem with ReadDataItem.
//
//    Eric Brugger, Fri Mar 21 15:22:11 PDT 2008
//    I added a buffer data type argument to the call to ReadDataItem.
//
//    Eric Brugger, Mon Apr 14 16:50:42 PDT 2008
//    Added support for tensors.
//
// ****************************************************************************

vtkDataArray *
avtVisItXdmfFileFormat::GetVectorVar(int domain, const char *varname)
{
    debug2 << "avtVisItXdmfFileFormat::GetVectorVar: domain = " << domain
           << ",varname = " << varname << endl;

    //
    // Determine the variable to return.
    //
    VarInfo *varInfo = NULL;
    for (unsigned int i = 0; i < fileVarList.size(); i++)
    {
        if (strcmp(fileVarList[i]->name.c_str(), varname) == 0)
        {
            varInfo = fileVarList[i];
            break;
        }
    }
    if (varInfo == NULL)
    {
        avtCallback::IssueWarning("GetVectorVar: Invalid variable name.");
        return NULL;
    }
    if (varInfo->blockList.size() > 0)
    {
        if (domain >= varInfo->blockList.size())
        {
            return NULL;
        }
        else
        {
            varInfo = varInfo->blockList[domain];
            if (varInfo == NULL)
            {
                return NULL;
            }
        }
    }

    //
    // The last dimension is the number of components in the vector, so
    // the variable must have at least 2 dimensions.
    //
    if (varInfo->varData->nDims < 2)
    {
        avtCallback::IssueWarning("GetVectorVar: Not enough dimensions.");
        return NULL;
    }

    //
    // Determine the size of the data.
    //
    int ntuples = 1;
    for (int i = 0; i < varInfo->varData->nDims - 1; i++)
        ntuples *= varInfo->varData->dims[i];
    int ncomps = varInfo->varData->dims[varInfo->varData->nDims-1];
    int ucomps = (ncomps == 2 ? 3 : ncomps);
    ucomps = (ucomps == 6 ? 9 : ucomps);

    //
    // Create the data array.
    //
    vtkFloatArray *rv = vtkFloatArray::New();
    rv->SetNumberOfComponents(ucomps);
    rv->SetNumberOfTuples(ntuples);

    //
    // Read the data.
    //
    float *buf = new float[ntuples*ncomps];
    if (!ReadDataItem(varInfo->varData, buf, ntuples * ncomps, VTK_FLOAT))
    {
        avtCallback::IssueWarning("GetVectorVar: Error reading the variable.");
        delete [] buf;
        rv->Delete();
        return NULL;
    }

    //
    // Copy the data into the vtk array.
    //
    if (ncomps == 6)
    {
        //
        // Handle the symmetric tensor case.
        //
        float *one_entry = new float[ucomps];
        for (int i = 0; i < ntuples; i++)
        {
            one_entry[0] = buf[i*ncomps];
            one_entry[1] = buf[i*ncomps+1];
            one_entry[2] = buf[i*ncomps+2];
            one_entry[3] = buf[i*ncomps+1];
            one_entry[4] = buf[i*ncomps+3];
            one_entry[5] = buf[i*ncomps+4];
            one_entry[6] = buf[i*ncomps+2];
            one_entry[7] = buf[i*ncomps+4];
            one_entry[8] = buf[i*ncomps+5];
            rv->SetTuple(i, one_entry); 
        }
        delete [] one_entry;
    }
    else
    {
        //
        // Handle the other cases.
        //
        float *one_entry = new float[ucomps];
        for (int j = ncomps; j < ucomps; j++)
            one_entry[j] = 0.;
        for (int i = 0; i < ntuples; i++)
        {
            for (int j = 0; j < ncomps; j++)
                one_entry[j] = buf[i*ncomps+j];
            rv->SetTuple(i, one_entry); 
        }
        delete [] one_entry;
    }

    delete [] buf;

    return rv;
}

// ****************************************************************************
//  Method: avtVisItXdmfFileFormat::GetTime
//
//  Programmer: Mark C. Miller
//  Created:    Thu Jul 30 11:12:53 PDT 2009
//
// ****************************************************************************

double avtVisItXdmfFileFormat::GetTime()
{
    if (fileMeshList.size())
        return fileMeshList[0]->time;
    else
        return avtFileFormat::INVALID_TIME;
}
